package com.lidy.tankbattle.tankgame2.tank;

import com.lidy.tankbattle.tankgame2.bomb.Bomb;
import com.lidy.tankbattle.tankgame2.constant.TankPriorities;
import com.lidy.tankbattle.tankgame2.constant.WallPriorities;
import com.lidy.tankbattle.tankgame2.wall.Wall;

import java.time.Duration;
import java.time.LocalTime;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Map;
import java.util.Random;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @author lidy
 * @version V2.0
 * @classname TankMove
 * @description 控制坦克移动
 * @date 2022/6/4 14:22
 */
public class TankMove {


    public static final int RECTANGLE_EDGE_NUMBER = 8;


    /**
     * @author: lidy
     * @description: 己方坦克移动
     * @date: 2022/6/2 9:56
     */
    public static void heroTankMove(int destDirection, Hero hero, ConcurrentHashMap<Integer, EnemyTank> enemyTanks,
                              ConcurrentHashMap<Integer, EntityLocation> tanksLocations, ConcurrentHashMap<Integer, Wall> walls, ConcurrentHashMap<Integer, Bomb> bombs) {
        if (destDirection == hero.getDirection()) {
            boolean detectionResult = true;
            EntityLocation playerTankLocation = getSingleTankLocation(hero);
            switch (hero.getDirection()) {
                case TankPriorities.DIRECTION_UP -> {
                    playerTankLocation.setYUp(playerTankLocation.getYUp() - hero.getSpeed());
                    playerTankLocation.setYDown(playerTankLocation.getYDown() - hero.getSpeed());
                }
                case TankPriorities.DIRECTION_DOWN -> {
                    playerTankLocation.setYUp(playerTankLocation.getYUp() + hero.getSpeed());
                    playerTankLocation.setYDown(playerTankLocation.getYDown() + hero.getSpeed());
                }
                case TankPriorities.DIRECTION_LEFT -> {
                    playerTankLocation.setXLeft(playerTankLocation.getXLeft() - hero.getSpeed());
                    playerTankLocation.setXRight(playerTankLocation.getXRight() - hero.getSpeed());
                }
                default -> {
                    playerTankLocation.setXLeft(playerTankLocation.getXLeft() + hero.getSpeed());
                    playerTankLocation.setXRight(playerTankLocation.getXRight() + hero.getSpeed());
                }
            }
            for (Map.Entry<Integer, Wall> wallEntry : walls.entrySet()) {
                Wall wall = wallEntry.getValue();
                if (wall.getExist() && TankMove.twoRectanglesIntersectionDetect(playerTankLocation, new EntityLocation(wall.getX(),
                        wall.getX() + WallPriorities.WALL_WIDTH, wall.getY(), wall.getY()+ WallPriorities.WALL_HEIGHT))) {
                    detectionResult = false;
                    break;
                }
            }
            if (detectionResult) {
                for (Map.Entry<Integer, EntityLocation> tankEntry : tanksLocations.entrySet()) {
                    int number = tankEntry.getKey();
                    EntityLocation tankLocation = tankEntry.getValue();
                    if (number != hero.getNumber() && TankMove.twoRectanglesIntersectionDetect(playerTankLocation, tankLocation)) {
                        hero.reduceBlood(TankPriorities.TANKS_BUMP_BLOOD_REDUCED);
                        EnemyTank enemyTank = enemyTanks.get(number);
                        enemyTank.reduceBlood(TankPriorities.TANKS_BUMP_BLOOD_REDUCED);
                        if (!enemyTank.getLive()) {
                            bombs.put(number, Bomb.getTankBomb(enemyTank.getX(), enemyTank.getY()));
                        }
                        if (!hero.getLive()) {
                            bombs.put(hero.getNumber(), Bomb.getTankBomb(hero.getX(), hero.getY()));
                        }
                        detectionResult = false;
                        break;
                    }
                }
            }

            if (detectionResult) {
                EntityLocation location = tanksLocations.get(hero.getNumber());
                int distance;
                switch (destDirection) {
                    case TankPriorities.DIRECTION_UP -> {
                        distance = Math.min(hero.getSpeed(), hero.getY());
                        hero.moveUp(distance);
                        location.setYUp(location.getYUp() - distance);
                        location.setYDown(location.getYDown() - distance);
                    }
                    case TankPriorities.DIRECTION_DOWN -> {
                        distance = Math.min(hero.getSpeed(), TankPriorities.PANEL_HEIGHT - hero.getY() - hero.getHeight());
                        hero.moveDown(distance);
                        location.setYUp(location.getYUp() + distance);
                        location.setYDown(location.getYDown() + distance);
                    }
                    case TankPriorities.DIRECTION_LEFT -> {
                        distance = Math.min(hero.getSpeed(), hero.getX());
                        hero.moveLeft(distance);
                        location.setXLeft(location.getXLeft() - distance);
                        location.setXRight(location.getXRight() - distance);
                    }
                    default -> {
                        distance = Math.min(hero.getSpeed(), TankPriorities.PANEL_WIDTH - hero.getX() - hero.getHeight());
                        hero.moveRight(distance);
                        location.setXLeft(location.getXLeft() + distance);
                        location.setXRight(location.getXRight() + distance);
                    }
                }
            }
        } else {
            hero.setDirection(destDirection);
        }
    }

    /**
     * @author: lidy
     * @description: 坦克自由移动
     * @date: 2022/6/3 10:40
     */
    public static int enemyTankMove(EnemyTank tank, ConcurrentHashMap<Integer, EntityLocation> tanksLocations, ConcurrentHashMap<Integer, Wall> walls) {
        // 修正坦克无敌时间
        if (tank.getInvincible()) {
            Duration duration = Duration.between(tank.getCreatedTime(), LocalTime.now());
            tank.setInvincible(duration.getSeconds() <= TankPriorities.TANK_INVINCIBLE_TIME);
        }
        // 剩余可移动距离为0，则改变方向
        if (tank.getRemainderLength() <= 0) {
            return changeDirection(tank, tanksLocations, walls);
            // 否则继续沿此方向移动，并判断是否合法
        } else {
            int directionLegalityDetection = directionLegalityDetection(tank, tank.getDirection(), tanksLocations, walls);
            if (directionLegalityDetection == 0 || directionLegalityDetection == 1) {
                EntityLocation location = tanksLocations.get(tank.getNumber());
                switch (tank.getDirection()) {
                    case TankPriorities.DIRECTION_DOWN -> {
                        tank.moveDownSpeed();
                        location.setYUp(location.getYUp() + tank.getSpeed());
                        location.setYDown(location.getYDown() + tank.getSpeed());
                    }
                    case TankPriorities.DIRECTION_LEFT -> {
                        tank.moveLeftSpeed();
                        location.setXLeft(location.getXLeft() - tank.getSpeed());
                        location.setXRight(location.getXRight() - tank.getSpeed());
                    }
                    case TankPriorities.DIRECTION_RIGHT -> {
                        tank.moveRightSpeed();
                        location.setXLeft(location.getXLeft() + tank.getSpeed());
                        location.setXRight(location.getXRight() + tank.getSpeed());
                    }
                    default -> {
                        tank.moveUpSpeed();
                        location.setYUp(location.getYUp() - tank.getSpeed());
                        location.setYDown(location.getYDown() - tank.getSpeed());
                    }
                }
                if (directionLegalityDetection == 0) {
                    // 更新当前方向上的持续移动距离
                    tank.setRemainderLength(tank.getRemainderLength() - tank.getSpeed());
                }
                return directionLegalityDetection;
            } else {
                return changeDirection(tank, tanksLocations, walls);
            }
        }
    }

    /**
     * @author: lidy
     * @description: 获取单个坦克位置信息
     * @date: 2022/6/5 11:17
     */
    public static EntityLocation getSingleTankLocation(Tank tank) {
        int xL = tank.getX();
        int xR = xL + switch (tank.getDirection()) {
            case TankPriorities.DIRECTION_UP, TankPriorities.DIRECTION_DOWN -> tank.getWidth();
            default -> tank.getHeight();
        };
        int yU = tank.getY();
        int yD = yU + switch (tank.getDirection()) {
            case TankPriorities.DIRECTION_UP, TankPriorities.DIRECTION_DOWN -> tank.getHeight();
            default -> tank.getWidth();
        };
        return new EntityLocation(xL, xR, yU, yD);
    }

    /**
     * @author: lidy
     * @description: 变换方向，更新剩余可移动距离
     * @date: 2022/6/3 10:54
     */
    private static int changeDirection(EnemyTank tank, ConcurrentHashMap<Integer, EntityLocation> tanksLocations, ConcurrentHashMap<Integer, Wall> walls) {
        ArrayList<Integer> enableDirections = new ArrayList<>(TankPriorities.ENABLE_DIRECTIONS);
        Collections.shuffle(enableDirections);
        int index = 0;
        int loopTimes = 0;
        while (index < enableDirections.size() && loopTimes < 3) {
            int nextDirection = enableDirections.get(index);
            int directionLegalityDetection = directionLegalityDetection(tank, nextDirection, tanksLocations, walls);
            switch (directionLegalityDetection) {
                case 0 -> {
                    tank.setDirection(nextDirection);
                    tank.setRemainderLength(calculateRemainderLength(tank));
                    return 0;
                }
                case 1 -> {
                    tank.setDirection(nextDirection);
                    return 1;
                }
                default -> {
                    index++;
                    if (index == enableDirections.size()) {
                        index = 0;
                        loopTimes++;
                    }
                }
            }
        }
        return 0;
    }

    /**
     * @return 0表示前方没有物体，1表示前方是玩家坦克，2表示前方向有敌方坦克，3表示前方是墙
     * @author: lidy
     * @description: 方向是否合理检测
     * @date: 2022/6/3 17:29
     */
    public static int directionLegalityDetection(Tank tank, int direction, ConcurrentHashMap<Integer, EntityLocation> tanksLocations, ConcurrentHashMap<Integer, Wall> walls) {
        int xL, xR, yU, yD;
        boolean outOfBounds;
        switch (direction) {
            case TankPriorities.DIRECTION_UP -> {
                xL = tank.getX();
                xR = xL + tank.getWidth();
                yU = tank.getY() - tank.getSpeed();
                yD = yU + tank.getHeight();
                outOfBounds = yU < 0;
            }
            case TankPriorities.DIRECTION_DOWN -> {
                xL = tank.getX();
                xR = xL + tank.getWidth();
                yU = tank.getY() + tank.getSpeed();
                yD = yU + tank.getHeight();
                outOfBounds = yD > TankPriorities.PANEL_HEIGHT;
            }
            case TankPriorities.DIRECTION_LEFT -> {
                xL = tank.getX() - tank.getSpeed();
                xR = xL + tank.getHeight();
                yU = tank.getY();
                yD = tank.getY() + tank.getWidth();
                outOfBounds = xL < 0;
            }
            default -> {
                xL = tank.getX() + tank.getSpeed();
                xR = xL + tank.getHeight();
                yU = tank.getY();
                yD = tank.getY() + tank.getWidth();
                outOfBounds = xR > TankPriorities.PANEL_WIDTH;
            }
        }
        // 边界检测
        if (outOfBounds) {
            return -1;
        }
        EntityLocation tankLocation = new EntityLocation(xL, xR, yU, yD);
        // 墙体检测
        for (Map.Entry<Integer, Wall> entry : walls.entrySet()) {
            Wall wall = entry.getValue();
            if (wall.getExist()) {
                if (twoRectanglesIntersectionDetect(tankLocation, new EntityLocation(wall.getX(),
                        wall.getX() + WallPriorities.WALL_WIDTH, wall.getY(), wall.getY() + WallPriorities.WALL_HEIGHT))) {
                    return 3;
                }
            }
        }

        // 坦克之间是否相撞检测
        for (Map.Entry<Integer, EntityLocation> entry : tanksLocations.entrySet()) {
            if (entry.getKey() != tank.getNumber() && twoRectanglesIntersectionDetect(tankLocation, entry.getValue())) {
                return entry.getKey() == TankPriorities.MY_TANK_NUMBER ? 1 : 2;
            }
        }

        return 0;
    }


    /**
     * @return true表示有交集，false表示没有交集
     * @author: lidy
     * @description: 两个矩形是否有交集检测
     * @date: 2022/6/4 10:58
     */
    public static boolean twoRectanglesIntersectionDetect(EntityLocation first, EntityLocation second) {
        int xL, xR, yU, yD;
        for (int i = 0; i < RECTANGLE_EDGE_NUMBER; i++) {
            switch (i) {
                case 0 -> {
                    xL = first.getXLeft();
                    xR = first.getXRight();
                    yU = first.getYUp();
                    yD = first.getYUp();
                }
                case 1 -> {
                    xL = first.getXLeft();
                    xR = first.getXLeft();
                    yU = first.getYUp();
                    yD = first.getYDown();
                }
                case 2 -> {
                    xL = first.getXRight();
                    xR = first.getXRight();
                    yU = first.getYUp();
                    yD = first.getYDown();
                }
                case 3 -> {
                    xL = first.getXLeft();
                    xR = first.getXRight();
                    yU = first.getYDown();
                    yD = first.getYDown();
                }
                case 4 -> {
                    xL = second.getXLeft();
                    xR = second.getXRight();
                    yU = second.getYUp();
                    yD = second.getYUp();
                }
                case 5 -> {
                    xL = second.getXLeft();
                    xR = second.getXLeft();
                    yU = second.getYUp();
                    yD = second.getYDown();
                }
                case 6 -> {
                    xL = second.getXRight();
                    xR = second.getXRight();
                    yU = second.getYUp();
                    yD = second.getYDown();
                }
                default -> {
                    xL = second.getXLeft();
                    xR = second.getXRight();
                    yU = second.getYDown();
                    yD = second.getYDown();
                }
            }
            if (i < 4) {
                if (!(xR <= second.getXLeft() || xL >= second.getXRight()) && !(yD <= second.getYUp() || yU >= second.getYDown())) {
                    return true;
                }
            } else {
                if (!(xR <= first.getXLeft() || xL >= first.getXRight()) && !(yD <= first.getYUp() || yU >= first.getYDown())) {
                    return true;
                }
            }
        }
        return first.getXLeft() == second.getXLeft() && first.getXRight() == second.getXRight() && first.getYUp() == second.getYUp() && first.getYDown() == second.getYDown();
    }

    /**
     * @author: lidy
     * @description: 计算当前方向剩余可行走距离
     * @date: 2022/6/4 9:52
     */
    private static int calculateRemainderLength(EnemyTank tank) {
        Random random = new Random(10);
        int ans = switch (tank.getDirection()) {
            case TankPriorities.DIRECTION_UP -> random.nextInt(tank.getY());
            case TankPriorities.DIRECTION_DOWN ->
                    random.nextInt(TankPriorities.PANEL_HEIGHT - tank.getHeight() - tank.getY());
            case TankPriorities.DIRECTION_LEFT -> random.nextInt(tank.getX());
            default -> random.nextInt(TankPriorities.PANEL_WIDTH - tank.getWidth() - tank.getX());
        };
        return ans + 20;
    }
}
